package com.sduept.i1.parse;

import com.google.common.io.LittleEndianDataOutputStream;
import com.sduept.i1.model.Apdu;
import com.sduept.i1.model.WeatherData;
import com.sduept.i1.model.MonitorPacket;
import com.sduept.i1.model.RemoteUpgradeData;
import com.sduept.i1.model.TowerTiltData;
import com.sduept.tool.CdzlbTools;
import com.sduept.tool.CrcUtil;
import com.sduept.tool.PrintTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;

public class Encoder {
	private static Logger logger = LoggerFactory.getLogger(Encoder.class);

	public static boolean sendUdpData(MonitorPacket monitorPacket) {
		try {
			Apdu outApdu = monitorPacket.outApduQueue.poll();
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(27 + outApdu.msgLen);
			LittleEndianDataOutputStream lo = new LittleEndianDataOutputStream(byteArrayOutputStream);
			lo.writeShort(MonitorPacket.FRAME_HEADER);
			lo.writeShort((short) (outApdu.msgLen & 0xFFFF));
			lo.write(monitorPacket.identification.getBytes());
			lo.writeByte((byte) (outApdu.frameType & 0xFF));
			lo.writeByte((byte) (outApdu.msgType & 0xFF));
			lo.writeByte((byte) (outApdu.frameNo & 0xFF));
			if (outApdu.msgLen > 0) {
				lo.write(outApdu.elements);
			}

			int crc16 = CrcUtil.calcCrc16(byteArrayOutputStream.toByteArray(), 2, 22 + outApdu.msgLen);
			lo.writeShort((short) (crc16 & 0xFFFF));
			lo.writeByte((byte) MonitorPacket.FRAME_END);
			byte[] sendArray = byteArrayOutputStream.toByteArray();

			String testMode = "true";// Jboot.configValue("test.mode");
			if (testMode != null) { // 测试模式报文打印到日志
				logger.info("ip:{}, port:{}, id:{}, send: {}", monitorPacket.lastNode.getIp(),
						monitorPacket.lastNode.getPort(), monitorPacket.identification, PrintTool.bytes2hex(sendArray));
			}
			lo.close();
			DatagramPacket datagramPacket = new DatagramPacket(sendArray, sendArray.length,
					new InetSocketAddress(monitorPacket.lastNode.getIp(), monitorPacket.lastNode.getPort())); // Integer.parseInt(Jboot.configValue("client.udp.port"))
			try {
				monitorPacket.datagramSocket.send(datagramPacket);
			} catch (Exception e) {
				logger.error("发送UDP失败", e);
				return false;
			}
			logger.info("monitorPacket.outApduQueue.size={}", monitorPacket.outApduQueue.size());

		} catch (Exception e) {
			logger.error("Encoder异常", e);
			return false;
		}

		return true;
	}

	// **************************************************************************************************************************
	// 客户端使用-组织检测数据报文
	// **************************************************************************************************************************

	// 组织一个微气象的数据包
	// 帧类型 01H
	// 报文类型 01H
	public static MonitorPacket makeMiniMeteorPacket(String deviceId, WeatherData data) {
		MonitorPacket monitorPacket = new MonitorPacket();
		monitorPacket.identification = deviceId;
		Apdu outApdu = new Apdu();
		outApdu.msgLen = WeatherData.msgLen;
		outApdu.frameNo = 1;
		outApdu.frameType = 0x01;
		outApdu.msgType = 0x01;
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(outApdu.msgLen);
		LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(byteArrayOutputStream);
		try {
			if (outApdu.msgLen > 0) {
				stream.write(data.getComponentId().getBytes());
				stream.writeInt(data.getTimeStamp());
				stream.writeFloat(data.getAverageWindSpeed10min());
				stream.writeShort(data.getAverageWindDirection10min());
				stream.writeFloat(data.getMaxWindSpeed());
				stream.writeFloat(data.getExtremeWindSpeed());
				stream.writeFloat(data.getStandardWindSpeed());
				stream.writeFloat(data.getAirTemperature());
				stream.writeShort(data.getHumidity());
				stream.writeFloat(data.getAirPressure());
				stream.writeFloat(data.getPrecipitation());
				stream.writeFloat(data.getPrecipitationIntensity());
				stream.writeShort(data.getRadiationIntensity());
			}
			outApdu.elements = byteArrayOutputStream.toByteArray();
			monitorPacket.outApduQueue.add(outApdu);
			stream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return monitorPacket;
	}

	// 组织一个杆塔倾斜的数据包
	// 帧类型 01H
	// 报文类型 02H
	public static MonitorPacket makeTowerTiltPacket(String deviceId, TowerTiltData data) {
		MonitorPacket monitorPacket = new MonitorPacket();
		monitorPacket.identification = deviceId;
		Apdu outApdu = new Apdu();
		outApdu.msgLen = TowerTiltData.msgLen;
		outApdu.frameNo = 1;
		outApdu.frameType = 0x01;
		outApdu.msgType = 0x02;
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(outApdu.msgLen);
		LittleEndianDataOutputStream stream = new LittleEndianDataOutputStream(byteArrayOutputStream);
		try {
			if (outApdu.msgLen > 0) {
				stream.write(data.getComponentId().getBytes());
				stream.writeInt(data.getTimeStamp());
				stream.writeFloat(data.getInclination());
				stream.writeFloat(data.getInclinationX());
				stream.writeFloat(data.getInclinationY());
				stream.writeFloat(data.getAngleX());
				stream.writeFloat(data.getAngleY());
			}
			outApdu.elements = byteArrayOutputStream.toByteArray();
			monitorPacket.outApduQueue.add(outApdu);
			stream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return monitorPacket;
	}

	// **************************************************************************************************************************
	// 服务端使用-组织控制数据报文
	// **************************************************************************************************************************

	// 装置时间查询设置
	// 帧类型 03H
	// 报文类型 A1H
	public static MonitorPacket makeClockTimePacket(String deviceId, int requestSetFlag, String timeStr) {
		int msgLen = 5;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// clocktimeStamp 4 设置时间，采用的的是 Unix 时间戳

			output.writeByte(requestSetFlag);
			if (requestSetFlag == 0) {
				output.writeInt(0);
			} else {
				SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				long timeStamp;
				try {
					timeStamp = dataFormat.parse(timeStr).getTime();
				} catch (ParseException e) {
					logger.error("Encoder.makeClockTimePacket: Date Format Parse Error");
					output.close();
					return null;
				}
				output.writeInt((int) (timeStamp / 1000));
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeClockTimePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA1, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 网络适配器查询与设置
	// 帧类型 03H
	// 报文类型 A2H
	public static MonitorPacket makeNetAdapterPacket(String deviceId, int requestSetFlag, int requestFlag,
			String[] values) {
		int msgLen = 38;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// requestFlag 1 标识位，字节自低向高依次对应各选择请求或设置的参数项，1表示选择，0表示未选择
			// ip 4 IP地址
			// subnetMask 4 子网掩码
			// gateway 4 网关
			// dnsServer 4 DNS服务器
			// phoneNumber 20 手机串号

			output.writeByte(requestSetFlag);
			output.writeByte(requestFlag);
			if (requestSetFlag == 0 || values.length != 5) {
				output.write(new byte[36]);
			} else {
				for (int i = 0; i < 5; i++) {
					// 解析 ip、子网掩码、网关、DNS 服务器
					// 将 a.b.c.d 解析成 byte 类型的 a b c d
					if (i != 4) {
						if (((requestFlag >> i) & 1) == 1) {
							String[] temp = values[i].split(".");
							for (int j = 0; j < 4; j++) {
								output.writeByte(Integer.parseInt(temp[j]));
							}
						} else {
							output.write(new byte[4]);
						}
					}
					// 手机串号
					else {
						if (((requestFlag >> 4) & 1) == 1) {
							byte[] phoneNumber = values[4].getBytes("ASCII");
							output.write(phoneNumber);
							output.write(new byte[20 - phoneNumber.length]);
						} else {
							output.write(new byte[20]);
						}
					}
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeNetAdapterPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA2, msgLen, byteStream.toByteArray()));

		return packet;

	}

	// 上级设备请求状态检测装置历史数据
	// 帧类型 03H
	// 报文类型 A3H
	public static MonitorPacket makeHistoryDataPacket(String deviceId, int requestType, String startTimeStr,
			String endTimeStr) {
		int msgLen = 9;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestType 1 请求数据类型
			// dataStartTime 4 请求历史数据的开始时间
			// dataEndTime 4 请求历史数据的结束时间

			output.writeByte(requestType);
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			long startTimeStamp;
			long endTimeStamp;
			try {
				startTimeStamp = df.parse(startTimeStr).getTime();
				endTimeStamp = df.parse(endTimeStr).getTime();
			} catch (ParseException e) {
				logger.error("Encoder.makeHistoryDataPacket: Date Format Parse Error");
				output.close();
				return null;
			}
			output.writeInt((int) (startTimeStamp / 1000));
			output.writeInt((int) (endTimeStamp / 1000));
			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeHistoryDataPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA3, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 状态检测装置采样参数查询与设置
	// 帧类型 03H
	// 报文类型 A4H
	public static MonitorPacket makeCollectPeriodPacket(String deviceId, int requestSetFlag, int requestType,
			int requestFlag, int[] values) {
		int msgLen = 10;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// requestType 1 配置的参数类型
			// requestFlag 1 标识位：字节自低向高依次对应各选择请求或设置的参数项
			// mainTime 2 采集时间周期
			// sampleCount 2 高速采样点数
			// sampleFrequency 2 高速采样频率
			// heartbeatTime 1 心跳上送周期

			output.writeByte(requestSetFlag);
			output.writeByte(requestType);
			output.writeByte(requestFlag);

			if (requestSetFlag == 0) {
				output.write(new byte[7]);
			} else {
				int valueIndex = 0;

				for (int i = 0; i < 4; i++) {
					if (((requestFlag >> i) & 1) == 0) {
						output.writeShort(0);
					} else if (i < 3) {
						output.writeShort(values[valueIndex++]);
					} else {
						output.writeByte(values[valueIndex]);
					}
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeCollectPeriodPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA4, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 状态检测装置模型参数配置信息查询与设置
	// 帧类型 03H
	// 报文类型 A5H
	public static MonitorPacket makeParamConfigPacket(String deviceId, int requestSetFlag, int configTotal,
			String[] configNames, int[] configDataTypes, int[] configDatas) {
		int msgLen = 2 + configTotal * 11;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// configTotal 1 配置参数个数
			// configName1 6 第一个配置参数的名称（小写字母与数字）
			// configDataType1 1 第一个配置参数的数据类型 00H无符号整数 01H有符号整数 02H单精度浮点数
			// configData1 4 第一个配置参数的值
			// ...
			// configNameN 6 第N个配置参数的名称
			// configDataTypeN 1 第N个配置参数的数据类型
			// configDataN 4 第N个配置参数的值

			output.writeByte(requestSetFlag);
			output.writeByte(configTotal);

			for (int i = 0; i < configTotal; i++) {
				byte[] configNameByte = configNames[i].getBytes("ASCII");

				// 查询信息时不需要读取 configDataType 和 configData
				output.write(configNameByte);
				if (requestSetFlag == 0) {
					output.write(new byte[11 - configNameByte.length]);
				} else {
					output.write(new byte[6 - configNameByte.length]);
					output.writeByte(configDataTypes[i]);
					output.writeInt(configDatas[i]);
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeParamConfigPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA5, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 报警阈值查询与设置
	// 帧类型 03H
	// 报文类型 A6H
	public static MonitorPacket makeAlarmThresholdPacket(String deviceId, int requestSetFlag, int requestType,
			int alarmTotal, String[] alarmNames, int[] alarmDatas) {
		int msgLen = 3 + alarmTotal * 10;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// requestType 1 配置的参数类型
			// alarmTotal 1 报警参数个数
			// alarmPara 10N 报警参数信息 6 参数值 4

			output.writeByte(requestSetFlag);
			output.writeByte(requestType);
			output.writeInt(alarmTotal);

			for (int i = 0; i < alarmTotal; i++) {
				byte[] alarmNameByte = alarmNames[i].getBytes("ASCII");
				output.write(alarmNameByte);
				if (requestSetFlag == 0) {
					output.write(new byte[10 - alarmNameByte.length]);
				} else {
					output.write(new byte[6 - alarmNameByte.length]);
					output.writeInt(alarmDatas[i]);
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeAlarmThresholdPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA6, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 监测装置指向主站系统的信息查询与设置
	// 帧类型 03H
	// 报文类型 A7
	public static MonitorPacket makeDevicePointPacket(String deviceId, int requestSetFlag, int requestFlag,
			String[] values) {
		int msgLen = 72;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询，01H表示设置
			// requestFlag 1 标识位，字节未自低向高依次对应各个选择请求或设置的参数项 1表示选择 0表示未选择
			// ipAddress 4 主站系统ip地址
			// port 2 主站系统端口号
			// domainName 64 主站系统域名，以'\0'结尾的ASCII字符串，占用64字节

			output.writeByte(requestSetFlag);
			output.writeByte(requestFlag);

			int valueIndex = 0;

			if ((requestFlag & 1) == 1) {
				String[] ipPart = values[valueIndex++].split(".");
				for (int i = 0; i < 4; i++) {
					output.writeByte(Integer.parseInt(ipPart[i]));
				}
			}

			if (((requestFlag >> 1) & 1) == 1) {
				output.writeShort(Integer.parseInt(values[valueIndex++]));
			}

			if (((requestFlag >> 2) & 1) == 1) {
				output.write(values[valueIndex].getBytes("ASCII"));
				output.writeByte('\0');
				output.write(new byte[63 - values[valueIndex].length()]);
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeDevicePointPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA7, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 基本信息查询与设置
	// 帧类型 03H
	// 报文类型 A8H
	public static MonitorPacket makeInfoTypePacket(String deviceId, int requestSetFlag, int requestType,
			int infoType) {
		int msgLen = 3;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询 01H表示设置
			// requestType 1 配置的参数类型
			// infoType 1 信息类型 01H基本信息 02H状态信息

			output.writeByte(requestSetFlag);
			output.writeByte(requestType);
			output.writeByte(infoType);
			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeBasicInfoPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xA8, msgLen, byteStream.toByteArray()));
		return packet;
	}

	// 远程升级
	// 帧类型 03H
	// 报文类型 A9H
	public static ArrayList<MonitorPacket> makeFilePacketArray(String deviceId, String fileName) {
		ArrayList<MonitorPacket> packetArray = new ArrayList<>();

		// 读取文件内容
		byte[] fileContent;
		try {
			fileContent = CdzlbTools.getContent(fileName);
		} catch (IOException e) {
			logger.error("Encoder.makeFilePacketArray: Get File Content Error");
			return packetArray;
		}

		// 将文件名字符串转换为字节数组
		byte[] fileNameBytes;
		try {
			fileNameBytes = (fileName + '\0').getBytes("ASCII");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encoder.makeFilePacketArray: Get Bytes Error");
			return packetArray;
		}

		// 检测文件名长度是否符合要求
		if (fileNameBytes.length > 20) {
			logger.error("Encoder.makeFilePacketArray: File Name Too Long");
			return packetArray;
		}

		// 数据区最大长度
		int maxSampleLength = RemoteUpgradeData.dataLength;

		// 报文数量（此时采用向下取整）
		int packetNo = fileContent.length / maxSampleLength;

		// 最后一个报文数据区有效内容的长度
		int lastPacketSampleLength = fileContent.length % maxSampleLength;
		if (lastPacketSampleLength == 0) {
			lastPacketSampleLength = maxSampleLength;
		} else {
			packetNo = packetNo + 1;
		}

		for (int i = 0; i < packetNo; i++) {
			int sampleLength = maxSampleLength;
			if (i == packetNo - 1) {
				sampleLength = lastPacketSampleLength;
			}

			// 字节数组使用数据区的最大长度，保证最后一个报文的长度与其他报文保持一致
			byte[] fileSample = new byte[maxSampleLength];
			for (int j = 0; j < sampleLength; j++) {
				fileSample[j] = fileContent[i * maxSampleLength + j];
			}

			int msgLen = maxSampleLength + 28;
			ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
			LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

			try {
				// fileName 20 文件名，以'\0'结尾的ASCII字符串
				// packetNo 4 总包数
				// subpacketNo 4 子包包号 从 0 开始计数
				// sample N 数据区

				output.write(fileNameBytes);
				output.write(new byte[20 - fileNameBytes.length]);
				output.writeInt(packetNo);
				output.writeInt(i);
				output.write(fileSample);

				output.close();
			} catch (IOException e) {
				logger.error("Encoder.makeFilePacketArray: Data Output Stream Write Error");
				return null;
			}

			MonitorPacket packet = new MonitorPacket();
			packet.identification = deviceId;
			packet.outApduQueue.offer(new Apdu(0x03, 0xA9, msgLen, byteStream.toByteArray()));
			packetArray.add(packet);
		}

		return packetArray;
	}

	// 文件传输结束标志
	// 帧类型 03H
	// 报文类型 AAH
	public static MonitorPacket makeFileEndPackage(String deviceId, String fileName, int packetNo) {
		// 将文件名字符串转换为字节数组
		byte[] fileNameBytes;
		try {
			fileNameBytes = (fileName + '\0').getBytes("ASCII");
		} catch (UnsupportedEncodingException e1) {
			logger.error("Encoder.makeFileEndPacket: Get Bytes Error");
			return null;
		}

		// 检测文件名长度是否符合要求
		if (fileNameBytes.length > 20) {
			logger.error("Encoder.makeFileEndPacket: File Name Too Long");
			return null;
		}

		int msgLen = 28;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// fileName 20 文件名 以'\0'结尾的ASCII字符串
			// packetNo 4 总包数
			// timeStamp 4 时间

			output.write(fileNameBytes);
			output.write(new byte[20 - fileNameBytes.length]);
			output.writeInt(packetNo);
			output.writeInt(0);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeFileEndPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xAA, msgLen, byteStream.toByteArray()));
		return packet;
	}

	// 补包
	// 帧类型 03H
	// 报文类型 ABH
	public static ArrayList<MonitorPacket> makeFileComplementPacketArray(String deviceId, String fileName,
			int[] complementPackNo) {
		ArrayList<MonitorPacket> packetArray = new ArrayList<>();

		// 读取文件内容
		byte[] fileContent;
		try {
			fileContent = CdzlbTools.getContent(fileName);
		} catch (IOException e) {
			logger.error("Encoder.makeFileSupplementPacketArray: Get File Content Error");
			return packetArray;
		}

		// 将文件名字符串转化为字节数组
		byte[] fileNameBytes;
		try {
			fileNameBytes = (fileName + '\0').getBytes("ASCII");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encoder.makeFileSupplementPacketArray: Get Bytes Error");
			return packetArray;
		}

		// 检测文件名长度是否符合要求
		if (fileNameBytes.length > 20) {
			logger.error("Encoder.makeFileSupplementPacketArray: File Name Too Long");
			return packetArray;
		}

		// 数据区最大长度
		int maxSampleLength = RemoteUpgradeData.dataLength;

		// 报文数量（此时是数据区有效内容达到最大长度的报文的数量）
		int packetNo = fileContent.length / maxSampleLength;

		int complementPackLength = complementPackNo.length;
		for (int i = 0; i < complementPackLength; i++) {
			int sampleLength = maxSampleLength;

			// 条件发生时，代表当前包号指向的最后一个报文，且最后一个报文数据区未达到最大长度
			// 与发送完整内容的报文不同，最后一个报文不一定需要发送，故把有效报文长度的计算放在条件语句后
			if (complementPackNo[i] == packetNo) {
				sampleLength = fileContent.length % maxSampleLength;
			}

			byte[] fileSample = new byte[maxSampleLength];
			for (int j = 0; j < sampleLength; j++) {
				fileSample[j] = fileContent[complementPackNo[i] * maxSampleLength + j];
			}

			int msgLen = maxSampleLength + 28;
			ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
			LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

			try {
				// fileName 20 文件名，以'\0'结尾的ASCII字符串
				// packetNo 4 总包数
				// subpacketNo 4 子包包号
				// sample N 数据区

				output.write(fileNameBytes);
				output.write(new byte[20 - fileNameBytes.length]);
				output.writeInt(packetNo);
				output.writeInt(complementPackNo[i]);
				output.write(fileSample);

				output.close();
			} catch (IOException e) {
				logger.error("Encoder.makeFileSupplementPacketArray: Data Output Stream Write Error");
				return null;
			}

			MonitorPacket packet = new MonitorPacket();
			packet.identification = deviceId;
			packet.outApduQueue.offer(new Apdu(0x03, 0xA9, msgLen, byteStream.toByteArray()));
			packetArray.add(packet);
		}

		return packetArray;
	}

	// 监测装置编码查询与设置
	// 帧类型 03H
	// 报文类型 ACH
	public static MonitorPacket makeDeviceIdPacket(String deviceId, int requestSetFlag, int requestFlag,
			String[] values) {
		int msgLen = 53;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H表示查询 01H表示设置
			// requestFlag 1 标识位 字节位自低向高依次对应各个选择请求或设置的参数项
			// newCmdId 17 状态检测装置新设置的ID
			// componentId 17 被监测设备新设置的ID
			// originalId 17 原始ID，不可更改

			output.writeByte(requestSetFlag);
			output.writeByte(requestFlag);

			if (requestSetFlag == 0) {
				output.write(new byte[51]);
			} else {
				int valueIndex = 0;

				// 因为 originalId 不可更改，所以只遍历前两个参数
				for (int i = 0; i < 2; i++) {
					if (((requestFlag >> i) & 1) == 1) {
						byte[] idBytes = values[valueIndex++].getBytes("ASCII");
						if (idBytes.length != 17) {
							logger.error("Encoder.makeDeviceIdPacket: Id Length Error");
							output.close();
							return null;
						}
						output.write(idBytes);
					}
				}
			}

			output.close();
		} catch (IOException e) {
			logger.info("Encoder.makeDeviceIdPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xAC, msgLen, byteStream.toByteArray()));
		return packet;
	}

	// 装置复位数据报文
	// 帧类型 03H
	// 报文类型 ADH
	public static MonitorPacket makeResetPacket(String deviceId, int resetMode) {
		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xAD, 1, new byte[] { (byte) resetMode }));

		return packet;
	}

	// 装置苏醒时间数据报文
	// 帧类型 03H
	// 报文类型 AEH
	public static MonitorPacket makeAwakeTimePacket(String deviceId, String timeStr, int revivalCycle,
			int durationTime) {
		int msgLen = 8;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// referenceRevivalTime 4 监测装置苏醒参考时间
			// revivalCycle 2 监测装置苏醒周期
			// durationTime 2 监测装置苏醒时间长度

			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			long timeStamp;
			try {
				timeStamp = dateFormat.parse(timeStr).getTime();
			} catch (ParseException e) {
				logger.error("Encoder.makeClockTimePacket: Date Format Parse Error");
				output.close();
				return null;
			}
			output.writeInt((int) (timeStamp / 1000));
			output.writeShort(revivalCycle);
			output.writeShort(durationTime);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeAwakeTimePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xAE, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 远程升级请求报
	// 帧类型 03H
	// 报文类型 B8H
	public static MonitorPacket makeRemoteUpdateRequestPacket(String deviceId, String fileName, int versionCode) {
		// 读取文件内容
		byte[] fileContent;
		try {
			fileContent = CdzlbTools.getContent(fileName);
		} catch (IOException e) {
			logger.error("Encoder.makeRemoteUpdateRequestPacket: Get File Content Error");
			return null;
		}

		// 将文件名字符串转换为字节数组
		byte[] fileNameBytes;
		try {
			fileNameBytes = (fileName + '\0').getBytes("ASCII");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encoder.makeRemoteUpgradeRequestPacket: Get Bytes Error");
			return null;
		}

		// 检测文件名长度是否符合要求
		if (fileNameBytes.length > 20) {
			logger.error("Encoder.makeRemoteUpgradeRequestPacket: File Name Too Long");
			return null;
		}

		// 数据区最大长度
		int maxSampleLength = RemoteUpgradeData.dataLength;

		// 报文数量
		int packetNo = fileContent.length / maxSampleLength;
		if (fileContent.length % maxSampleLength != 0) {
			packetNo++;
		}

		int msgLen = 40;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// fileSize 4 文件的字节大小
			// fileName 20 文件名
			// versionCode 4 升级包的版本
			// packetHigh 2 包数高位
			// packetLow 2 包数低位
			// reserve 8 备用

			output.writeInt(fileContent.length);
			output.write(fileNameBytes);
			output.writeInt(versionCode);
			output.writeShort(packetNo >> 16);
			output.writeShort(packetNo);
			output.write(new byte[8]);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeRemoteUpgradeRequestPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xB8, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 主站发送装置功能开启和关闭报文
	// 帧类型 03H
	// 报文类型 B9H
	public static MonitorPacket makeFunctionControlPacket(String deviceId, int functionType, int control) {
		int msgLen = 23;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// functionType 2 功能分类 01H声光告警 02H-Wifi功能 其他
			// control 1 00H停用 01H启用
			// reserve 20 备用

			output.writeShort(functionType);
			output.writeByte(control);
			output.write(new byte[20]);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeFunctionControlPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xB9, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// HTTP升级文件推送
	// 帧类型 03H
	// 报文类型 BAH
	public static MonitorPacket makeHttpUpgradePushPacket(String deviceId, int fileType, String httpUrl,
			String patchPath) {
		int msgLen = 521;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// fileType 1 文件类型 0程序升级包 1系统包 2补丁 3智能识别模型
			// httpUrl 260 HTTP升级文件链接
			// patchPath 260 仅针对补丁有效

			output.writeByte(fileType);

			byte[] httpUrlByte, patchPathByte;
			try {
				httpUrlByte = httpUrl.getBytes("ASCII");
				patchPathByte = patchPath.getBytes("ASCII");
			} catch (UnsupportedEncodingException e) {
				logger.error("Encoder.makeHttpUpgradePushPacket: Get Bytes Error");
				output.close();
				return null;
			}

			output.write(httpUrlByte);
			output.write(patchPathByte);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeHttpUpgradePushPacket: Data Output Stream Write Error");
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xBA, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 主站发送摄像机3D控球调节命令报文
	// 帧类型 03H
	// 报文类型 BBH
	public static MonitorPacket make3DBallControlPacket(String deviceId, int channelNo, int startX, int startY,
			int endX, int endY) {
		int msgLen = 5;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// startX 1 起点x坐标
			// startY 1 起点y坐标
			// endX 1 终点y坐标
			// endY 1 终点y坐标

			output.writeByte(channelNo);
			output.writeByte(startX);
			output.writeByte(startY);
			output.writeByte(endX);
			output.writeByte(endY);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.make3DBallControlPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xBB, msgLen, byteStream.toByteArray()));

		return null;
	}

	// 摄像机巡检配置
	// 帧类型 03H
	// 报文类型 BCH
	public static MonitorPacket makeCameraCruiseSetPacket(String deviceId, int channelNo, int cmd, int cruiseNo,
			int pointerNo) {
		int msgLen = 4;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// cmd 1 配置指令
			// cruiseNo 1 巡检组号
			// pointerNo 1 巡检点序号

			output.writeByte(channelNo);
			output.writeByte(cmd);
			output.writeByte(cruiseNo);
			output.writeByte(pointerNo);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeCameraCruisePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xBC, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 摄像机巡检查询
	// 帧类型 03H
	// 报文类型 BDH
	public static MonitorPacket makeCameraCruiseGetPacket(String deviceId, int channelNo) {
		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xBD, 1, new byte[] { (byte) channelNo }));

		return packet;
	}

	// 摄影机巡检策略查询与设置
	// 帧类型 03H
	// 报文类型 BEH
	public static MonitorPacket makeCameraCruiseStrategyPacket(String deviceId, int channelNo, int requestSetFlag,
			int configNum, int[] enables, int[] cruiseNos, int[] cruiseNums, int[] startHours, int[] startMinutes,
			int[] startSeconds) {
		int msgLen = 3 + configNum * 6;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// requestSetFlag 1 参数配置类型标识 00H查询 01H设置
			// configNum 1 线路巡检配置数目
			// enable_i 1 巡检使能标志
			// cruiseNo_i 1 巡检组号
			// cruiseNum_i 1 巡检次数
			// cruiseStartTime_i 3 每天巡检开始时间 格式；时 分 秒

			output.writeByte(channelNo);
			output.writeByte(requestSetFlag);
			output.writeByte(configNum);
			for (int i = 0; i < configNum; i++) {
				output.writeByte(enables[i]);
				output.writeByte(cruiseNos[i]);
				if (requestSetFlag == 0x00) {
					output.write(new byte[4]);
				} else {
					output.writeByte(cruiseNums[i]);
					output.writeByte(startHours[i]);
					output.writeByte(startMinutes[i]);
					output.writeByte(startSeconds[i]);
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeCameraCruiseStrategyPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x03, 0xBE, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// **************************************************************************************************************************
	// 客户端使用-组织图像数据报文
	// **************************************************************************************************************************

	// 装置发送上送图像/短视频报文的请求
	// 帧类型 05H
	// 报文类型 EFH
	public static MonitorPacket makeRequestUploadImagePacket(String deviceId) {
		/* 模拟数据 */
		byte channelNo = 1;
		byte packetHigh = 0;
		byte packetLow = 3;
		int imageId = 0;

		int msgLen = 11;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// packetHigh 1 包数高位
			// packet 1 包数低位
			// imageId 4 内容ID
			// reserve 4 备用

			output.writeByte(channelNo);
			output.writeByte(packetHigh);
			output.writeByte(packetLow);
			output.writeInt(imageId);
			output.write(new byte[4]);

			output.close();
		} catch (Exception e) {
			logger.error("Encoder.makeRequestUploadImagePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x05, 0xEF, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 装置发送远程图像数据报文
	// 帧类型 05H
	// 报文类型 F0H
	public static ArrayList<MonitorPacket> makeImageDataPacket(String deviceId) {
		ArrayList<MonitorPacket> packetArray = new ArrayList<>();

		/* 模拟数据 */
		byte channelNo = 1;
		short packetNo = 2;
		int imageId = 1;
		int imageOffset = 0;
		byte[][] imageData = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10 } };

		for (int subpacketNo = 0; subpacketNo < packetNo; subpacketNo++) {
			int msgLen = 5 + imageData[subpacketNo].length;
			ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
			LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

			try {
				// channelNo 1 通道号
				// packetNo 2 总包数
				// subpacketNo 2 子包包号
				// sample N 数据区（前4个字节位图像ID，接下来4个字节是该包数据在图像数据中的偏移量）

				output.writeByte(channelNo);
				output.writeShort(packetNo);
				output.writeShort(subpacketNo);
				output.writeInt(imageId);
				output.writeInt(imageOffset++);
				output.write(imageData[subpacketNo]);

				output.close();
			} catch (Exception e) {
				logger.error("Encoder.makeImageDataPacket: Data Output Stream Write Error");
				continue;
			}

			MonitorPacket packet = new MonitorPacket();
			packet.identification = deviceId;
			packet.outApduQueue.offer(new Apdu(0x05, 0xF0, msgLen, byteStream.toByteArray()));

			packetArray.add(packet);
		}

		return packetArray;
	}

	// 装置发送图像数据上送结束标记报文
	// 帧类型 05H
	// 报文类型 F1H
	public static MonitorPacket makeImageUploadFinishPacket(String deviceId) {
		/* 模拟数据 */
		byte channelNo = 1;
		int timeStamp = 4;
		byte[] md5 = new byte[32];
		byte fileType = 1;

		int msgLen = 45;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// timeStamp 4 本图像/视频拍摄时间
			// MD5 32 文件MD5码
			// fileType 1 文件类型 0图片 1视频
			// reserve 7 备用

			output.writeByte(channelNo);
			output.writeInt(timeStamp);
			output.write(md5);
			output.writeByte(fileType);
			output.write(new byte[7]);

			output.close();
		} catch (Exception e) {
			logger.error("Encoder.makeImageUploadFinishPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x05, 0xF1, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// **************************************************************************************************************************
	// 服务端使用-组织图像控制报文
	// **************************************************************************************************************************

	// 摄像机远程调节命令
	// 帧类型 07H
	// 报文类型 D0H
	public static MonitorPacket makeRemoteActionPacket(String deviceId, int channelNo, int presettingNo, int action) {
		int msgLen = 3;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号 表示装置上的摄像机编号
			// presettingNo 1 预置位号，即云台摄像所设置的预置位号
			// action 1 动作指令

			output.writeByte(channelNo);
			output.writeByte(presettingNo);
			output.writeByte(action);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeRemoteActionPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xD0, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 图像参数查询/设置
	// 帧类型 07H
	// 报文类型 EBH
	public static MonitorPacket makeImageParamPacket(String deviceId, int requestSetFlag, int requestFlag,
			int[] values, int channelNo, int preset) {

		int msgLen = 15;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H查询配置信息 01H设置配置信息
			// requestFlag 1 标识位，字节由低向高依次对应各项选择请求或设置的参数项 1选择 0未选择
			// colorSelect 1 色彩选择
			// resolution 1 自定义图像分辨率
			// luminance 1 亮度
			// contrast 1 对比度
			// saturation 1 饱和度
			// channelNo 1 通道号
			// preset 1 预置位

			output.writeByte(requestSetFlag);
			output.writeByte(requestFlag);

			if (requestSetFlag == 0) {
				output.write(new byte[5]);
			} else {
				int valueIndex = 0;
				for (int i = 0; i < 5; i++) {
					if (((requestFlag >> i) & 1) == 1) {
						output.write(values[valueIndex++]);
					} else {
						output.writeByte(0);
					}
				}
			}

			output.writeByte(channelNo);
			output.writeByte(preset);

			output.close();
		} catch (Exception e) {
			logger.info("Encoder.makeImageParamSetPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xE8, msgLen, byteStream.toByteArray()));
		return packet;
	}

	// 短视频参数查询/设置
	// 帧类型 07H
	// 报文类型 ECH
	public static MonitorPacket makeVideoParamPacket(String deviceId, int requestSetFlag, int channelNo,
			int videoFormat, int videoTime) {
		int msgLen = 5;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H查询配置信息 01H设置配置信息
			// channelNo 1 通道号，表示装置上的摄像机编号
			// videoFormat 1 视频格式
			// videoTime 2 视频录制时间，单位秒

			output.writeByte(requestSetFlag);
			output.writeByte(channelNo);
			output.writeByte(videoFormat);
			output.writeShort(videoTime);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeVideoParamSetPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xEC, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 拍摄时间查询/设置
	// 帧类型 07H
	// 报文类型 EDH
	public static MonitorPacket makeShootTimePacket(String deviceId, int requestSetFlag, int channelNo, int hour1,
			int minute1, int hour2, int minute2, int hour3, int minute3) {
		int msgLen = 8;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// requestSetFlag 1 参数配置类型标识 00H查询配置信息 01H设置配置信息
			// channelNo 1 通道号，表示采集装置上的摄像机编号
			// huor1 1 设备拍照时间间隔小时
			// minute1 1 设备拍照时间间隔分钟
			// hour2 1 设备开始工作时间小时
			// minute2 1 设备开始工作时间分钟
			// hour3 1 设备结束工作时间小时
			// minue2 1 设备结束工作时间分钟

			output.writeByte(requestSetFlag);
			output.writeByte(channelNo);
			output.writeByte(hour1);
			output.writeByte(minute1);
			output.writeByte(hour2);
			output.writeByte(minute2);
			output.writeByte(hour3);
			output.writeByte(minute3);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeShootTimeSetPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xED, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 手动请求拍摄照片/短视频
	// 帧类型 07H
	// 报文类型 EEH
	public static MonitorPacket makeRequestShootPacket(String deviceId, int channelNo, int dataType) {
		int msgLen = 11;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// dataType 1 数据类型 0为图片，1为短视频
			// reserve 9 备用

			output.writeByte(channelNo);
			output.writeByte(dataType);
			output.write(new byte[msgLen - 2]);

			output.close();
		} catch (Exception e) {
			logger.error("Encoder.makeRequestShootPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xEE, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 图像OSD查询/设置
	// 帧类型 07H
	// 报文类型 F3H
	public static MonitorPacket makeOsdPacket(String deviceId, int channelNo, int requestSetFlag, int showTime,
			int showText, String textContent) {

		byte[] textContentBytes;
		try {
			textContentBytes = (textContent + '\0').getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			logger.error("Encoder.makeOsdPacket: Get Bytes Error");
			return null;
		}

		int msgLen = 4 + textContentBytes.length;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// requestSetFlag 1 参数配置类型标识 00H查询 01H设置
			// showTime 1 是否显示时间标识 0不显示 1显示
			// showText 1 文本显示标识 0不显示 1显示
			// textContent N 文本内容，UTF-8编码格式，以'\0'结尾

			output.writeByte(channelNo);
			output.writeByte(requestSetFlag);
			output.writeByte(showTime);
			output.writeByte(showText);
			output.write(textContentBytes);

			output.close();
		} catch (IOException e) {
			logger.info("Encoder.makeOsdPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xF3, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 图像分析参数设置
	// 帧类型 07H
	// 报文类型 F5H
	public static MonitorPacket makeImageAnalysisParamSetPacket(String deviceId, int channelNo, int preset,
			int analysisFlag, int alarmNum, int[] alarmTypes, int[] alarmThresholds, int alarmAreaNum, int[] areaFlags,
			int[] areaPointNums, int[][] areaPointXs, int[][] areaPointYs) {
		int msgLen = 5 + alarmNum * 2 + alarmAreaNum * 2;
		for (int i = 0; i < alarmAreaNum; i++) {
			msgLen += areaPointNums[i] * 2;
		}
		
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// channelNo 1 通道号
			// preset 1 预置位号
			// analysisFlag 1 智能分析启用标志
			// alarmNum 1 告警类型数量
			// alarmType_i 1 告警i类型
			// alarmThreshold_i 1 告警i阈值
			// alarmAreaNum 1 告警区域数量
			// areaFlag_j 1 区域j作用标志
			// areaPointNum_j 1 区域j坐标点数目
			// areaPointX_j_k 1 区域j坐标点k横坐标
			// areaPointY_j_k 1 区域j坐标点k纵坐标

			output.writeByte(channelNo);
			output.writeByte(preset);
			output.writeByte(analysisFlag);
			output.writeByte(alarmNum);
			for (int i = 0; i < alarmNum; i++) {
				output.writeByte(alarmTypes[i]);
				output.writeByte(alarmThresholds[i]);
			}
			output.writeByte(alarmAreaNum);
			for (int j = 0; j < alarmAreaNum; j++) {
				output.writeByte(areaFlags[j]);
				int pointNum = areaPointNums[j];
				output.writeByte(pointNum);
				for (int k = 0; k < pointNum; k++) {
					output.writeByte(areaPointXs[j][k]);
					output.writeByte(areaPointYs[j][k]);
				}
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeImageAnalysisParamSetPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xF5, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 图像分析参数查询
	// 帧类型 07H
	// 报文类型 F6H
	public static MonitorPacket makeImageAnalysisParamGetPacket(String deviceId, int paramNum, int[] channelNos,
			int[] presets) {
		int msgLen = 1 + paramNum * 2;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// paramNum 1 数据域参数组数量
			// dataSource N 数据域 每组参数两字节 字节1通道号，从1开始计数，字节2位预置位 FFH查询所有

			output.writeByte(paramNum);
			for (int i = 0; i < paramNum; i++) {
				output.writeByte(channelNos[i]);
				output.writeByte(presets[i]);
			}

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeImageAnalysisParamGetPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x07, 0xF6, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// **************************************************************************************************************************
	// 客户端使用-组织工作状态控制数据报文
	// **************************************************************************************************************************

	// 装置发送心跳数据报文
	// 帧类型 09H
	// 报文类型 E6H
	public static MonitorPacket makeHeartbeatPacket(String deviceId) {
		int msgLen = 4;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// clocktimeStamp 4 当前时间

			output.writeInt((int) (System.currentTimeMillis() / 1000));

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeHeartbeatPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x09, 0xE6, msgLen, byteStream.toByteArray()));
		return packet;
	}

	// 工作状态报
	// 帧类型 09H
	// 报文类型 E8H
	public static MonitorPacket makeWorkStatePacket(String deviceId) {
		/* 模拟数据 */
		int timeStamp = 1;
		int batteryVoltage = 1;
		int operationTemperature = 1;
		int batteryCapacity = 1;
		int floatingCharge = 1;
		int totalWorkingTime = 1;
		int workingTime = 1;
		int connectionState = 1;

		int msgLen = 26;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// timeStamp 4 采集时间
			// batteryVoltage 4 电源电压
			// operationTemperature 4 工作温度
			// batteryCapacity 4 电池电量
			// floatingCharge 1 浮充状态 00H充电 01H放电
			// totalWorkingTime 4 工作总时间
			// WorkingTime 4 本次连续工作时间
			// ConnectionState 1 网络连接状态 00H正常 01H断开

			output.writeInt(timeStamp);
			output.writeFloat(batteryVoltage);
			output.writeInt(operationTemperature);
			output.writeInt(batteryCapacity);
			output.writeByte(floatingCharge);
			output.writeInt(totalWorkingTime);
			output.writeInt(workingTime);
			output.writeByte(connectionState);

			output.close();
		} catch (Exception e) {
			logger.error("Encoder.makeWorkStatePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket monitorPacket = new MonitorPacket();
		monitorPacket.identification = deviceId;
		monitorPacket.outApduQueue.add(new Apdu(0x09, 0xE8, msgLen, byteStream.toByteArray()));

		return monitorPacket;
	}

	// 故障描述报
	// 帧类型 09H
	// 报文类型 E9H
	public static MonitorPacket makeFaultDescPacket(String deviceId) {
		/* 模拟数据 */
		int timeStamp = 1;
		int n = 100;
		byte[] faultDescByte = ("fault description").getBytes();

		int msgLen = 4 + faultDescByte.length;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// timeStamp 4 采集时间
			// faultDesc N 返回监测装置故障描述信息

			output.writeInt(timeStamp);
			output.write(faultDescByte);
			output.write(new byte[n - faultDescByte.length]);

			output.close();
		} catch (IOException e) {
			logger.error("Encoder.makeFaultDescPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;

		return packet;
	}

	// 检测装置基本信息补充报
	// 帧类型 09H
	// 报文类型 FAH
	public static MonitorPacket makeAdditionalBasicInfoPacket(String deviceId) {
		/* 模拟数据 */
		int n = 2;
		int commandStatus = 0xFF;
		byte[] function = new byte[8];
		byte[] aiFunction = new byte[4];
		int channelGroup = n;
		byte[] channelNos = new byte[n];
		byte[] channelFuns = new byte[n];
		byte[] iccid = new byte[25];
		int operator = 1;
		byte[] protocolVersion = new byte[32];
		byte[] factoryFlag = new byte[8];
		byte[] systemVersion = new byte[n];
		byte[] reserve = new byte[20];

		int msgLen = 100 + n * 3;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// commandStatus 1 数据发送状态
			// function 8 功能类型
			// aiFunction 4 智能分析功能类型
			// channelGroup 1 通道数
			// channelNo_i 1 通道号
			// channelFun_i 1 通道功能
			// iccid 25 卡信息
			// operator 1 运营商
			// protocolVersion 32 协议版本号
			// factoryFlag 8 厂家标识
			// systemVersion N 系统版本号
			// reserve 20 备用

			output.writeByte(commandStatus);
			output.write(function);
			output.write(aiFunction);
			output.writeByte(channelGroup);
			for (int i = 0; i < n; i++) {
				output.writeByte(channelNos[i]);
				output.writeByte(channelFuns[i]);
			}
			output.write(iccid);
			output.writeByte(operator);
			output.write(protocolVersion);
			output.write(factoryFlag);
			output.write(systemVersion);
			output.write(reserve);

			output.close();
		} catch (IOException e) {
			logger.info("Encoder.makeAdditionalBasicInfoPacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x09, 0xFA, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// 检测装置工作状态补充报
	// 帧类型 09H
	// 报文类型 FBH
	public static MonitorPacket makeAdditionalWorkStatePacket(String deviceId) {
		/* 模拟数据 */
		int commandStatus = 1;
		int signalStrength = 1;
		int dbm = 1;
		int dbmMin = 0;
		int dbmMax = 2;
		float dayNetflow = 1.0F;
		float monthNetflow = 10.0F;
		byte[] function = new byte[8];
		byte[] reserve = new byte[20];

		int msgLen = 44;
		ByteArrayOutputStream byteStream = new ByteArrayOutputStream(msgLen);
		LittleEndianDataOutputStream output = new LittleEndianDataOutputStream(byteStream);

		try {
			// commandStatus 1 数据发送状态
			// SignalStrength 1 信号等级
			// dbm 2 信号强度实测值
			// dbmMin 2 DBM下限
			// dbmMax 2 DBM上限
			// dayNetflow 4 当日已用流量
			// MonthNetflow 4 当月已用流量
			// function 8 功能类型
			// reserve 20 备用

			output.writeByte(commandStatus);
			output.writeByte(signalStrength);
			output.writeShort(dbm);
			output.writeShort(dbmMin);
			output.writeShort(dbmMax);
			output.writeFloat(dayNetflow);
			output.writeFloat(monthNetflow);
			output.write(function);
			output.write(reserve);

			output.close();
		} catch (IOException e) {
			logger.info("Encoder.makeAdditionalWorkStatePacket: Data Output Stream Write Error");
			return null;
		}

		MonitorPacket packet = new MonitorPacket();
		packet.identification = deviceId;
		packet.outApduQueue.offer(new Apdu(0x09, 0xFB, msgLen, byteStream.toByteArray()));

		return packet;
	}

	// **************************************************************************************************************************
	// 服务端使用-组织工作状态查询报文
	// **************************************************************************************************************************

	// 查询监测装置基本信息补充
	// 帧类型 0AH
	// 报文类型 FAH
	public static MonitorPacket makeAdditionalBasicInfoGetPacket(String deviceId) {
		MonitorPacket monitorPacket = new MonitorPacket();
		monitorPacket.identification = deviceId;
		monitorPacket.outApduQueue.offer(new Apdu(0x0A, 0xFA, 1, new byte[] { 0 }));
		return monitorPacket;
	}

	// 查询监测装置工作状态补充
	// 帧类型 0AH
	// 报文类型 FBH
	public static MonitorPacket makeAdditionalWorkStateGetPacket(String deviceId) {
		MonitorPacket monitorPacket = new MonitorPacket();
		monitorPacket.identification = deviceId;
		monitorPacket.outApduQueue.offer(new Apdu(0x0A, 0xFA, 1, new byte[] { 0 }));
		return monitorPacket;
	}
}
